package mk.jug.academy.service;

import java.util.Date;

import mk.jug.academy.model.Content;
import mk.jug.academy.model.Lecturer;
import mk.jug.academy.model.Page;
import mk.jug.academy.model.User;
import mk.jug.academy.testing.bdd.IntegrationBehavior;
import mk.jug.academy.testing.factory.ContentTestFactory;
import mk.jug.academy.testing.factory.LecturerTestFactory;
import mk.jug.academy.testing.util.TestHelper;

import org.springframework.beans.factory.annotation.Autowired;

import com.googlecode.instinct.marker.annotate.Specification;
import com.googlecode.instinct.marker.annotate.Specification.SpecificationState;
import com.googlecode.instinct.marker.annotate.Subject;

/**
 * An integration test unit targeting the behavior regarding the
 * publication and management of {@link Content}s submitted by
 * {@link User}s of the system.
 *
 * @author Ice.Penov
 */
public class ContentManagement extends IntegrationBehavior {
	
	@Autowired private ContentService contentService;
	@Autowired private LecturerService lecturerService;
		
	@Subject private Lecturer lecturer;
	@Subject private Lecturer lecturer1;
	@Subject private Lecturer lecturer2;
	@Subject private Content content;
	//for paging
	@Subject private Content content1;
	@Subject private Content content2;
	@Subject private Content content3;
	
	@Override
	protected void beforeHook() {
		lecturer = LecturerTestFactory.createLecturer();
		lecturer1 = LecturerTestFactory.createLecturer();
		lecturer2 = LecturerTestFactory.createLecturer();
		content = ContentTestFactory.createContent();
		content1 = ContentTestFactory.createContent();
		content2 = ContentTestFactory.createContent();
		content3 = ContentTestFactory.createContent();
		
		TestHelper.init(lecturerService);
		TestHelper.init(contentService);
	}
	
	/**
	 * User action:
	 * A {@link User} saves a {@link Content}.
	 * 
	 * Expected behavior:
	 * 1. The content is listed as saved
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void saveContentByUser() {
		TestHelper.prepareActiveLecturer(lecturer);
		contentService.save(content, lecturer);
		//expectations.
		expect.that(content.getCreatorID()).isEqualTo(lecturer.getId());
		expect.that(content.getCreatorType().toString()).isEqualTo(lecturer.getRole());
		expect.that(content.getCreatorDisplayName()).isEqualTo(lecturer.getDisplayName());
		expect.that(contentService.listSaved(1, 2).getItems()).containsItem(content);
		expect.that(contentService.listSaved(1, 2).getItems()).isOfSize(1);
	}
	
	/**
	 * Action:
	 * A {@link Lecturer} publishes a saved {@link Content}.
	 * 
	 * Expectation:
	 * 1. The content should listed as published
	 * 2. The publish date should be appropriate
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void publishSavedContent() {
		TestHelper.prepareActiveLecturer(lecturer);
		Date refDate = new Date();
		contentService.save(content, lecturer);
		contentService.publish(content);
		//expectations.
		expect.that(content.getPublishDate()).isGreaterThanOrEqualTo(refDate);
		expect.that(contentService.listPublished(1, 2).getItems()).containsItem(content);
		expect.that(contentService.listPublished(1, 2).getItems()).isOfSize(1);
	}
		
	/**
	 * User action:
	 * A {@link Content} published by a {@link User} is being removed.
	 * 
	 * Expected behavior:
	 * 1. The content is listed as deleted
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void removePublishedContentByLecturer() {
		TestHelper.prepareActiveLecturer(lecturer);
		contentService.save(content, lecturer);
		contentService.publish(content);
		contentService.delete(content);
		//expectations.
		expect.that(contentService.listDeleted(1, 2).getItems()).containsItem(content);
		expect.that(contentService.listDeleted(1, 2).getItems()).isOfSize(1);
		expect.that(contentService.listPublished(1, 2).getItems()).isEmpty();
	}
		
	/**
	 * User action:
	 * Edit the {@link Content}s properties.
	 * 
	 * Expected behavior:
	 * 1. All changes to be saved.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void editContentProperties() {
		TestHelper.prepareActiveLecturer(lecturer);
		contentService.save(content, lecturer);
		content.setTitle("Changed");
		content.setBody("Changed");
		contentService.saveChanges(content);
		Content reloaded = contentService.load(content.getId());
		//expectations.
		expect.that(reloaded.getTitle()).isEqualTo(content.getTitle());
		expect.that(reloaded.getBody()).isEqualTo(content.getBody());
	}
	
	/**
	 * User action:
	 * List blogs by the their creator.
	 * 
	 * Expected behavior:
	 * Only blogs created for that user should be listed.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void listBlogsByPublishingUser() {
		TestHelper.prepareActiveLecturer(lecturer1);
		TestHelper.prepareActiveLecturer(lecturer2);
		contentService.save(content1, lecturer1);
		contentService.save(content2, lecturer2);
		contentService.save(content3, lecturer1);
		contentService.publish(content1);
		contentService.publish(content2);
		contentService.publish(content3);
		//open the pages
		//as a lecturer1:
		Page<Content> lecturer1Page = contentService.listPublishedAsCreator(1, 2, lecturer1.getId());
		expect.that(lecturer1Page.getItems()).containsItems(content1, content3);
		expect.that(lecturer1Page.getItems()).doesNotContainItem(content2);
		//as a lecturer2:
		Page<Content> lecturer2Page = contentService.listPublishedAsCreator(1, 2, lecturer2.getId());
		expect.that(lecturer2Page.getItems()).containsItem(content2);
		expect.that(lecturer2Page.getItems()).doesNotContainItems(content1, content3);		
		//check the delete listing.
		contentService.delete(content1);
		contentService.delete(content2);
		//as lecturer 1:
		lecturer1Page = contentService.listDeletedAsCreator(1, 2, lecturer1.getId());
		expect.that(lecturer1Page.getItems()).containsItem(content1);
		expect.that(lecturer1Page.getItems()).doesNotContainItems(content2, content3);
		//as lecturer 2:
		lecturer2Page = contentService.listDeletedAsCreator(1, 2, lecturer2.getId());
		expect.that(lecturer2Page.getItems()).containsItem(content2);
		expect.that(lecturer2Page.getItems()).doesNotContainItems(content1, content3);
	}
	
	/**
	 * User action:
	 * Fetch the content entries in a paged manner.
	 * 
	 * Expected behavior:
	 * List the records as expected.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void navigateContentUsingPaging() {
		TestHelper.prepareActiveLecturer(lecturer);
		contentService.save(content1, lecturer);
		contentService.save(content2, lecturer);
		contentService.save(content3, lecturer);
		contentService.publish(content1);
		contentService.publish(content2);
		contentService.publish(content3);
		//open the pages.
		Page<Content> page1 = contentService.listPublished(1, 2);
		Page<Content> page2 = contentService.listPublished(2, 2);
		Page<Content> page3 = contentService.listPublished(3, 2);
		//expectations.
		//size:
		expect.that(page1.getTotalCount()).isEqualTo(3);
		expect.that(page2.getTotalCount()).isEqualTo(3);
		expect.that(page3.getTotalCount()).isEqualTo(3);
		expect.that(page1.getItems()).isOfSize(2);
		expect.that(page2.getItems()).isOfSize(1);
		expect.that(page3.getItems()).isEmpty();
		//check contents of the pages:
		expect.that(page1.getItems()).containsItems(content3, content2);
		expect.that(page2.getItems()).containsItem(content1);
		
		//deleted records
		contentService.delete(content3);
		//expectations.
		page1 = contentService.listDeleted(1, 2);
		expect.that(page1.getItems()).isOfSize(1);
		expect.that(page1.getItems()).containsItem(content3);
	}
	
}